#include "NBT.h"
#include <fstream> // Used for file output (NOT input)
#include "Minecraft/Containers/Chunk.h"
using namespace NBT;

#pragma region Forward Declared Functions


#pragma region Read
unsigned int ReadEnd(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadByte(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadShort(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadInt(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadLong(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadFloat(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadDouble(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadByteArray(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadString(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadList(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);
unsigned int ReadCompound(const char *buff, ITAG *&newTag, bool bReadName = true, bool bChunk = false, Chunk* _chunk = NULL);

#pragma endregion

#pragma region Helper Helpers
unsigned int ReadName(const char *buff, ITAG *&NewTag);
inline unsigned  _int32 BitWarp16(unsigned _int16 &x);
inline unsigned _int32 BitWarp32(unsigned _int32 &x);
inline unsigned _int64 BitWarp64(unsigned _int64 &x);
char *Tabs(int count);
#pragma endregion

#pragma region Write
void WriteEndASCII(std::ofstream &ofs, TAG_End const *&tag, int depth);
void WriteByteASCII(std::ofstream &ofs, TAG_Byte const *&tag, int depth);
void WriteShortASCII(std::ofstream &ofs, TAG_Short const *&tag, int depth);
void WriteIntASCII(std::ofstream &ofs, TAG_Int const *&tag, int depth);
void WriteLongASCII(std::ofstream &ofs, TAG_Long const *&tag, int depth);
void WriteFloatASCII(std::ofstream &ofs, TAG_Float const *&tag, int depth);
void WriteDoubleASCII(std::ofstream &ofs, TAG_Double const *&tag, int depth);
void WriteByteArrayASCII(std::ofstream &ofs, TAG_Byte_Array const *&tag, int depth);
void WriteStringASCII(std::ofstream &ofs, TAG_String const *&tag, int depth);
void WriteListASCII(std::ofstream &ofs, TAG_List const *&tag, int depth);
void WriteCompoundASCII(std::ofstream &ofs, TAG_Compound const *&tag, int depth);
#pragma endregion

#pragma endregion

// Functions :

#pragma region NBT Namespace Functions
NBTFile *NBT::Decode(const char *buff, unsigned int length)
{
	NBTFile *outputFile = new NBTFile;
	NBT_TAG tType;
	ITAG *newTag;
	unsigned int pos = 0;

	while (pos < length)
	{
		tType = (NBT_TAG)buff[pos++];

		switch (tType)
		{
		case END:
			ReadEnd(buff + pos, newTag);
			break;
		case BYTE:
			pos += ReadByte(buff + pos, newTag);
			break;
		case SHORT:
			pos += ReadShort(buff + pos, newTag);
			break;
		case INT:
			pos += ReadInt(buff + pos, newTag);
			break;
		case LONG:
			pos += ReadLong(buff + pos, newTag);
			break;
		case FLOAT:
			pos += ReadFloat(buff + pos, newTag);
			break;
		case DOUBLE:
			pos += ReadDouble(buff + pos, newTag);
			break;
		case BYTE_ARRAY:
			pos += ReadByteArray(buff + pos, newTag);
			break;
		case STRING:
			pos += ReadString(buff + pos, newTag);
			break;
		case LIST:
			pos += ReadList(buff + pos, newTag);
			break;
		case COMPOUND:
			pos += ReadCompound(buff + pos, newTag);
			break;
		}

		outputFile->tags.push_back(newTag);
	}

	return outputFile;
}

void NBT::DecodeIntoChunk(const char *buff, unsigned int length, Chunk* _chunk)
{
	NBT_TAG tType;
	ITAG *newTag;
	unsigned int pos = 0;

	while (pos < length)
	{
		tType = (NBT_TAG)buff[pos++];

		switch (tType)
		{
		case END:
			ReadEnd(buff + pos, newTag, true, true);
			break;
		case BYTE:
			pos += ReadByte(buff + pos, newTag, true, true);
			break;
		case SHORT:
			pos += ReadShort(buff + pos, newTag, true, true);
			break;
		case INT:
			pos += ReadInt(buff + pos, newTag, true, true);
			break;
		case LONG:
			pos += ReadLong(buff + pos, newTag, true, true);
			break;
		case FLOAT:
			pos += ReadFloat(buff + pos, newTag, true, true);
			break;
		case DOUBLE:
			pos += ReadDouble(buff + pos, newTag, true, true);
			break;
		case BYTE_ARRAY:
			pos += ReadByteArray(buff + pos, newTag, true, true, _chunk);
			break;
		case STRING:
			pos += ReadString(buff + pos, newTag);
			break;
		case LIST:
			pos += ReadList(buff + pos, newTag);
			break;
		case COMPOUND:
			pos += ReadCompound(buff + pos, newTag, true, true, _chunk);
			break;
		}
	}
}

bool NBT::WriteToText(const char*szFile, NBTFile const *file)
{
	std::ofstream ofs;
	ofs.clear();
	ofs.open(szFile, std::ios_base::out | std::ios_base::trunc);
	if (ofs.good() == false)
		return false;

	std::list<ITAG *>::const_iterator tagIter = file->tags.begin();
	std::list<ITAG *>::const_iterator iterEnd = file->tags.end();
	for (; tagIter != iterEnd; ++tagIter)
	{
		switch ((*tagIter)->type)
		{
		case END:
			WriteEndASCII(ofs, (TAG_End const *&)(*tagIter), 0);
			break;
		case BYTE:
			WriteByteASCII(ofs, (TAG_Byte const *&)(*tagIter), 0);
			break;
		case SHORT:
			WriteShortASCII(ofs, (TAG_Short const *&)(*tagIter), 0);
			break;
		case INT:
			WriteIntASCII(ofs, (TAG_Int const *&)(*tagIter), 0);
			break;
		case LONG:
			WriteLongASCII(ofs, (TAG_Long const *&)(*tagIter), 0);
			break;
		case FLOAT:
			WriteFloatASCII(ofs, (TAG_Float const *&)(*tagIter), 0);
			break;
		case DOUBLE:
			WriteDoubleASCII(ofs, (TAG_Double const *&)(*tagIter), 0);
			break;
		case BYTE_ARRAY:
			WriteByteArrayASCII(ofs, (TAG_Byte_Array const *&)(*tagIter), 0);
			break;
		case STRING:
			WriteStringASCII(ofs, (TAG_String const *&)(*tagIter), 0);
			break;
		case LIST:
			WriteListASCII(ofs, (TAG_List const *&)(*tagIter), 0);
			break;
		case COMPOUND:
			WriteCompoundASCII(ofs, (TAG_Compound const *&)(*tagIter), 0);
			break;
		}
	}

	ofs.close();
	ofs.clear();
	return true;
}
#pragma endregion

// Helpers :

#pragma region Read
unsigned int ReadEnd(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_End();
	newTag->type = END;
	newTag->szName = NULL;
	return 0;
}

unsigned int ReadByte(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Byte();
	newTag->type = BYTE;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	((TAG_Byte *)newTag)->payload = buff[nLen];

	return nLen + 1;
}

unsigned int ReadShort(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Short();
	newTag->type = SHORT;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	unsigned _int16 sBuff = BitWarp16(*((unsigned _int16 *)(buff + nLen)));
	((TAG_Short *)newTag)->payload = sBuff;

	return nLen + 2;
}

unsigned int ReadInt(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Int();
	newTag->type = INT;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	unsigned _int32 iBuff = BitWarp32(*((unsigned _int32 *)(buff + nLen)));
	((TAG_Int *)newTag)->payload = iBuff;

	return nLen + 4;
}

unsigned int ReadLong(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Long();
	newTag->type = LONG;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	unsigned _int64 lBuff = *((unsigned _int64 *)(buff + nLen));
	BitWarp64(lBuff);
	((TAG_Long *)newTag)->payload = lBuff;

	return nLen + 8;
}

unsigned int ReadFloat(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Float();
	newTag->type = FLOAT;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	unsigned _int32 iBuff = BitWarp32(*(unsigned _int32 *)(buff + nLen));
	float fBuff = (float)(*(float *)&iBuff);
	((TAG_Float *)newTag)->payload = fBuff;

	return nLen + 4;
}

unsigned int ReadDouble(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Double();
	newTag->type = DOUBLE;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	unsigned _int64 iBuff = BitWarp64(*((unsigned _int64 *)(buff + nLen)));
	double dBuff = (double)(*(double *)&iBuff);

	((TAG_Double *)newTag)->payload = dBuff;

	return nLen + 8;
}

unsigned int ReadByteArray(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Byte_Array();
	TAG_Byte_Array *brTag = (TAG_Byte_Array *)newTag;
	newTag->type = BYTE_ARRAY;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	unsigned _int32 bLen = BitWarp32(*((unsigned _int32 *)(buff + nLen)));
	brTag->length = bLen;

	brTag->payload = new UBYTE[bLen];
	memcpy_s(brTag->payload, bLen, buff + nLen + 4, bLen);

	if(bChunk && bReadName)
	{
		if(strcmp(brTag->szName, "Blocks") == 0)
			_chunk->fillBlockId(brTag->payload);
		else if(strcmp(brTag->szName, "Data") == 0)
			_chunk->fillAdditionalData(brTag->payload);
		else if(strcmp(brTag->szName, "SkyLight") == 0)
			_chunk->fillSkylightData(brTag->payload);
		else if(strcmp(brTag->szName, "BlockLight") == 0)
			_chunk->fillBlocklightData(brTag->payload);
		else if(strcmp(brTag->szName, "HeightMap") == 0)
			_chunk->fillHeightMap(brTag->payload);
	}
	return nLen + bLen + 4;
}

unsigned int ReadString(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_String();
	newTag->type = STRING;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);


	unsigned _int16 strLen = BitWarp16(*((unsigned _int16 *)(buff + nLen)));
	nLen += 2;

	char *szBuff = new char[strLen + 1];
	memcpy_s(szBuff, strLen + 1, buff + nLen, strLen);
	szBuff[strLen] = '\0';

	((TAG_String *)newTag)->payload = szBuff;
	((TAG_String *)newTag)->length = strLen;

	return nLen + strLen;
}

unsigned int ReadList(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_List();
	newTag->type = LIST;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	NBT_TAG tType = (NBT_TAG)buff[nLen++];

	unsigned _int32 arrLen = BitWarp32(*((unsigned _int32 *)(buff + nLen)));
	nLen += 4;
	ITAG **payload = new ITAG*[arrLen];

	for (unsigned int iTag = 0; iTag < arrLen; ++iTag)
	{
		switch(tType)
		{
		case END: // This shouldn't happen, but whatever
			nLen += ReadEnd(buff + nLen, payload[iTag], false);
			break;
		case BYTE:
			nLen += ReadByte(buff + nLen, payload[iTag], false);
			break;
		case SHORT:
			nLen += ReadShort(buff + nLen, payload[iTag], false);
			break;
		case INT:
			nLen += ReadInt(buff + nLen, payload[iTag], false);
			break;
		case LONG:
			nLen += ReadLong(buff + nLen, payload[iTag], false);
			break;
		case FLOAT:
			nLen += ReadFloat(buff + nLen, payload[iTag], false);
			break;
		case DOUBLE:
			nLen += ReadDouble(buff + nLen, payload[iTag], false);
			break;
		case BYTE_ARRAY:
			nLen += ReadByteArray(buff + nLen, payload[iTag], false);
			break;
		case STRING:
			nLen += ReadString(buff + nLen, payload[iTag], false);
			break;
		case LIST:
			nLen += ReadList(buff + nLen, payload[iTag], false);
			break;
		case COMPOUND:
			nLen += ReadCompound(buff + nLen, payload[iTag], false);
			break;
		}
	}

	((TAG_List *)newTag)->tagIds = tType;
	((TAG_List *)newTag)->length = arrLen;
	((TAG_List *)newTag)->payload = payload;


	return nLen;
}

unsigned int ReadCompound(const char *buff, ITAG *&newTag, bool bReadName, bool bChunk, Chunk* _chunk)
{
	newTag = new TAG_Compound();
	newTag->type = COMPOUND;

	int nLen = 0;
	if (bReadName)
		nLen = ReadName(buff, newTag);

	NBT_TAG tType;
	bool foundEnd = false;
	ITAG *ourNewTag;

	while (foundEnd == false)
	{
		tType = (NBT_TAG)buff[nLen++];

		switch(tType)
		{
		case END: // This shouldn't happen, but whatever
			foundEnd = true;
			nLen += ReadEnd(buff + nLen, ourNewTag);
			break;
		case BYTE:
			nLen += ReadByte(buff + nLen, ourNewTag);
			break;
		case SHORT:
			nLen += ReadShort(buff + nLen, ourNewTag);
			break;
		case INT:
			nLen += ReadInt(buff + nLen, ourNewTag);
			break;
		case LONG:
			nLen += ReadLong(buff + nLen, ourNewTag);
			break;
		case FLOAT:
			nLen += ReadFloat(buff + nLen, ourNewTag);
			break;
		case DOUBLE:
			nLen += ReadDouble(buff + nLen, ourNewTag);
			break;
		case BYTE_ARRAY:
			nLen += ReadByteArray(buff + nLen, ourNewTag, bReadName, bChunk, _chunk);
			break;
		case STRING:
			nLen += ReadString(buff + nLen, ourNewTag);
			break;
		case LIST:
			nLen += ReadList(buff + nLen, ourNewTag);
			break;
		case COMPOUND:
			nLen += ReadCompound(buff + nLen, ourNewTag, bReadName, bChunk, _chunk);
			break;
		}

		((TAG_Compound * )newTag)->payload.push_back(ourNewTag);
	}

	return nLen;
}
#pragma endregion

#pragma region Helper Helpers
unsigned int ReadName(const char *buff, ITAG *&newTag)
{
	unsigned _int16 strLen = BitWarp16(*((unsigned _int16 *)buff));
	newTag->szName = new char[strLen + 1];
	memcpy_s(newTag->szName, strLen + 1, buff + 2, strLen);
	newTag->szName[strLen] = '\0';

	return strLen + 2;
}

inline unsigned  _int32 BitWarp16(unsigned _int16 &x)
{
	x = ((x) >> 8) | ((x) << 8);
	return x;
}

inline unsigned _int32 BitWarp32(unsigned _int32 &x)
{
	x = ((x) >> 24) | (((x) << 8) & 0x00FF0000) | (((x) >> 8) & 0x0000FF00) | ((x) << 24);
	return x;
}

inline unsigned _int64 BitWarp64(unsigned _int64 &x)
{
	x = (((x) >> 56) | 
		(((x) << 40) & 0x00FF000000000000) | 
		(((x) << 24) & 0x0000FF0000000000) | 
		(((x) << 8)  & 0x000000FF00000000) | 
		(((x) >> 8)  & 0x00000000FF000000) | 
		(((x) >> 24) & 0x0000000000FF0000) | 
		(((x) >> 40) & 0x000000000000FF00) | 
		((x) << 56));
	return x;
}

char *Tabs(int count)
{
	static char tabs[32];
	for (int i = 0; i < count && i < 32; ++i)
	{
		tabs[i] = '\t';
	}
	tabs[count] = '\0';

	return tabs;
}
#pragma endregion

#pragma region Write
void WriteEndASCII(std::ofstream &ofs, TAG_End const *&tag, int depth)
{
	ofs << "}\n";
}

void WriteByteASCII(std::ofstream &ofs, TAG_Byte const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Byte(\"" << tag->szName << "\") : " << (int)tag->payload << "('" << (char)tag->payload << "')\n";
	else
		ofs << "TAG_Byte : " << (int)tag->payload << "('" << (char)tag->payload << "')\n";
}

void WriteShortASCII(std::ofstream &ofs, TAG_Short const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Short(\"" << tag->szName << "\") : " << tag->payload << '\n';
	else
		ofs << "TAG_Short : " << tag->payload << '\n';
}

void WriteIntASCII(std::ofstream &ofs, TAG_Int const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Int(\"" << tag->szName << "\") : " << tag->payload << '\n';
	else
		ofs << "TAG_Int : " << tag->payload << '\n';
}

void WriteLongASCII(std::ofstream &ofs, TAG_Long const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Long(\"" << tag->szName << "\") : " << tag->payload << '\n';
	else
		ofs << "TAG_Long : " << tag->payload << '\n';
}

void WriteFloatASCII(std::ofstream &ofs, TAG_Float const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Float(\"" << tag->szName << "\") : " << tag->payload << '\n';
	else
		ofs << "TAG_Float : " << tag->payload << '\n';
}

void WriteDoubleASCII(std::ofstream &ofs, TAG_Double const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Double(\"" << tag->szName << "\") : " << tag->payload << '\n';
	else
		ofs << "TAG_Double : " << tag->payload << '\n';
}

void WriteByteArrayASCII(std::ofstream &ofs, TAG_Byte_Array const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_Byte_Array(\"" << tag->szName << "\") : [" << tag->length << " bytes]\n";
	else
		ofs << "TAG_Byte_Array : [" << tag->length << " bytes]\n";
	// TODO: output bytes?
}

void WriteStringASCII(std::ofstream &ofs, TAG_String const *&tag, int depth)
{
	if (tag->szName != NULL)
		ofs << "TAG_String(\"" << tag->szName << "\") : " << tag->payload << '\n';
	else
		ofs << "TAG_String : " << tag->payload << '\n';
}

void WriteListASCII(std::ofstream &ofs, TAG_List const *&tag, int depth)
{
	++depth;

	if (tag->szName != NULL)
		ofs << "TAG_List(\"" << tag->szName << "\") : " << tag->length << " entries of type ";
	else
		ofs << "TAG_List : " << tag->length << " entries of type ";

	switch (tag->tagIds)
	{
	case END:
		ofs << "TAG_End\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteEndASCII(ofs, (TAG_End const *&)(tag->payload[iTag]), depth);
		}
		break;
	case BYTE:
		ofs << "TAG_Byte\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteByteASCII(ofs, (TAG_Byte const *&)(tag->payload[iTag]), depth);
		}
		break;
	case SHORT:
		ofs << "TAG_Short\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteShortASCII(ofs, (TAG_Short const *&)(tag->payload[iTag]), depth);
		}
		break;
	case INT:
		ofs << "TAG_Int\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteIntASCII(ofs, (TAG_Int const *&)(tag->payload[iTag]), depth);
		}
		break;
	case LONG:
		ofs << "TAG_Long\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteLongASCII(ofs, (TAG_Long const *&)(tag->payload[iTag]), depth);
		}
		break;
	case FLOAT:
		ofs << "TAG_Float\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteFloatASCII(ofs, (TAG_Float const *&)(tag->payload[iTag]), depth);
		}
		break;
	case DOUBLE:
		ofs << "TAG_Double\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteDoubleASCII(ofs, (TAG_Double const *&)(tag->payload[iTag]), depth);
		}
		break;
	case BYTE_ARRAY:
		ofs << "TAG_Byte_Array\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteByteArrayASCII(ofs, (TAG_Byte_Array const *&)(tag->payload[iTag]), depth);
		}
		break;
	case STRING:
		ofs << "TAG_String\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteStringASCII(ofs, (TAG_String const *&)(tag->payload[iTag]), depth);
		}
		break;
	case LIST:
		ofs << "TAG_List\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteListASCII(ofs, (TAG_List const *&)(tag->payload[iTag]), depth);
		}
		break;
	case COMPOUND:
		ofs << "TAG_Compound\n";
		ofs << Tabs(depth - 1) << "{\n";
		for (int iTag = 0; iTag < tag->length; ++iTag)
		{
			ofs << Tabs(depth);
			WriteCompoundASCII(ofs, (TAG_Compound const *&)(tag->payload[iTag]), depth);
		}
		break;
	}
	ofs << Tabs(depth - 1) << "}\n";
}

void WriteCompoundASCII(std::ofstream &ofs, TAG_Compound const *&tag, int depth)
{
	++depth;
	if (tag->szName != NULL)
		ofs << "TAG_Compound(\"" << tag->szName << "\") : " << tag->payload.size() - 1 << " entries\n" << Tabs(depth - 1) << "{\n";
	else
		ofs << "TAG_Compound : " << tag->payload.size() << " entries\n" << Tabs(depth - 1) << "{\n";

	std::list<ITAG *>::const_iterator tagIter = tag->payload.begin();
	std::list<ITAG *>::const_iterator iterEnd = tag->payload.end();
	while (tagIter != iterEnd)
	{
		switch ((*tagIter)->type)
		{
		case END:
			ofs << Tabs(depth - 1);
			WriteEndASCII(ofs, (TAG_End const *&)(*tagIter), depth);
			break;
		case BYTE:
			ofs << Tabs(depth);
			WriteByteASCII(ofs, (TAG_Byte const *&)(*tagIter), depth);
			break;
		case SHORT:
			ofs << Tabs(depth);
			WriteShortASCII(ofs, (TAG_Short const *&)(*tagIter), depth);
			break;
		case INT:
			ofs << Tabs(depth);
			WriteIntASCII(ofs, (TAG_Int const *&)(*tagIter), depth);
			break;
		case LONG:
			ofs << Tabs(depth);
			WriteLongASCII(ofs, (TAG_Long const *&)(*tagIter), depth);
			break;
		case FLOAT:
			ofs << Tabs(depth);
			WriteFloatASCII(ofs, (TAG_Float const *&)(*tagIter), depth);
			break;
		case DOUBLE:
			ofs << Tabs(depth);
			WriteDoubleASCII(ofs, (TAG_Double const *&)(*tagIter), depth);
			break;
		case BYTE_ARRAY:
			ofs << Tabs(depth);
			WriteByteArrayASCII(ofs, (TAG_Byte_Array const *&)(*tagIter), depth);
			break;
		case STRING:
			ofs << Tabs(depth);
			WriteStringASCII(ofs, (TAG_String const *&)(*tagIter), depth);
			break;
		case LIST:
			ofs << Tabs(depth);
			WriteListASCII(ofs, (TAG_List const *&)(*tagIter), depth);
			break;
		case COMPOUND:
			ofs << Tabs(depth);
			WriteCompoundASCII(ofs, (TAG_Compound const *&)(*tagIter), depth);
			break;
		}
		++tagIter;
	}
}
#pragma endregion

// Member Functions :

#pragma region Destructors
NBTFile::~NBTFile()
{
	std::list<ITAG *>::iterator tagIter = tags.begin();
	std::list<ITAG *>::iterator iterEnd = tags.end();
	while (tagIter != iterEnd)
	{
		delete (*tagIter);
		++tagIter;
	}
	tags.clear();
}

ITAG::~ITAG()
{
	if (szName != NULL)
	{
		delete[] szName;
		szName = NULL;
	}
}

TAG_Byte_Array::~TAG_Byte_Array()
{
	if (payload != NULL)
	{
		delete[] payload;
		payload = NULL;
	}
}
TAG_String::~TAG_String()
{
	if (payload != NULL)
	{
		delete[] payload;
		payload = NULL;
	}
}

TAG_List::~TAG_List()
{
	if (payload != NULL)
	{
		for (_int32 i = 0; i < length; ++i)
		{
			if (payload[i] != NULL)
				delete payload[i];
		}

		delete[] payload;
		payload = NULL;
		length = 0;
	}
}

TAG_Compound::~TAG_Compound()
{
	std::list<ITAG *>::iterator tagIter = payload.begin();
	std::list<ITAG *>::iterator iterEnd = payload.end();
	while (tagIter != iterEnd)
	{
		delete (*tagIter);
		++tagIter;
	}
	payload.clear();
}
#pragma endregion
